#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

#include "my_types.h"
#include "fifo.h"
#include "scan.h"
#include "b1.h"
#include "rgbyuv.h"
#include "b2.h"
#include "dct.h"
#include "b3.h"
#include "b5.h"
#include "b4.h"
#include "quant.h"
#include "huf.h"
#include "fdump.h"

int main(int argc, char *argv[]) {

  fifo<D_PIXEL> scanR("scanR", 2),   scanG("scanG", 2),   scanB("scanB", 2);
  fifo<D_PIXEL> b1R("b1R", 513),       b1G("b1G", 513),       b1B("b1B", 513);
  fifo<D_PIXEL> rgbY("rgb", 513),      rgbU("rgbU", 513),     rgbV("rgbV", 513);
  fifo<D_PIXEL> b2Y1("b2Y1", 257),     b2Y2("b2Y2", 257),     b2U("b2U", 257),     b2V("b2V", 257);
  fifo<D_DCT> dctY1c("dctY1c", 257), dctY1r("dctY1r", 257), dctY1e("dctY1e", 257); 
  fifo<D_DCT> dctY2c("dctY2c", 257), dctY2r("dctY2r", 257), dctY2e("dctY2e", 257); 
  fifo<D_DCT> dctUc("dctUc", 257),   dctUr("dctUr", 257),   dctUe("dctUe", 257);  
  fifo<D_DCT> dctVc("dctVc", 257),   dctVr("dctVr", 257),   dctVe("dctVe", 257);  
  fifo<D_DCT> dctY1("dctY1", 257);
  fifo<D_DCT> dctY2("dctY2", 257);
  fifo<D_DCT> dctU("dctU", 257);
  fifo<D_DCT> dctV("dctV", 257);
  fifo<D_QUANT_OUT> qY1("qY1", 257),       qY2("qY2", 257),       qU("qU", 257),       qV("qV", 257);
  fifo<D_QUANT_OUT> b4Y1("b4Y1", 257),     b4Y2("b4Y2", 257),     b4U("b4U", 257),     b4V("b4V", 257);
  fifo<D_HUF_VAL> hufVal("hufVal", 257);
  fifo<D_HUF_BITS> hufBits("hufBits", 257);
  
int moretokens;

  if (argc < 3) {
    cout << "Usage: jpeg <infile> <outfile>" << endl;
    exit(0);
  }

  scan_init  (argv[1]);
  b1_init    (scan_x);
  rgbyuv_init();
  b2_init    ();
  dct1_init  ();
  b3_init    ();
  b5_init    ();
  quant_init ();
  b4_init    ();
  huf_init   ();
  fdump_init (argv[2],scan_x,scan_y);

  moretokens = 1;
  while ( moretokens ) {
    scan_run   (&scanR, &scanG, &scanB);

    moretokens = scanR.len();

    b1_run     (&scanR,  &scanG, &scanB, &b1R,  &b1G,  &b1B );

    rgbyuv_run (&b1R,    &b1G,   &b1B,   &rgbY, &rgbU, &rgbV);

    b2_run     (&rgbY,   &rgbU,  &rgbV,  &b2Y1, &b2Y2, &b2U,  &b2V);

    dct1_run   (&b2Y1,   0, &dctY1r, 0, 256);
    b5_run     (&dctY1r, &dctY1c);
    dct1_run   (0, &dctY1c, 0, &dctY1e,   0);
    b3_run     (&dctY1e, &dctY1);

    dct1_run   (&b2Y2,   0, &dctY2r, 0, 256);
    b5_run     (&dctY2r, &dctY2c);
    dct1_run   (0, &dctY2c, 0, &dctY2e,   0);
    b3_run     (&dctY2e, &dctY2);

    dct1_run   (&b2U,    0, &dctUr, 0, 256);
    b5_run     (&dctUr,  &dctUc);
    dct1_run   (0, &dctUc,  0, &dctUe,   0);
    b3_run     (&dctUe,  &dctU);

    dct1_run   (&b2V,    0, &dctVr, 0, 256);
    b5_run     (&dctVr,  &dctVc);
    dct1_run   (0, &dctVc,  0, &dctVe,   0);
    b3_run     (&dctVe,  &dctV);


    quant_l_run(&dctY1,  &qY1);
    quant_l_run(&dctY2,  &qY2);
    quant_c_run(&dctU,   &qU);
    quant_c_run(&dctV,   &qV);

    b4_run     (&qY1,    &b4Y1);
    b4_run     (&qY2,    &b4Y2);
    b4_run     (&qU,     &b4U);
    b4_run     (&qV,     &b4V);

    huf_l_run  (&b4Y1,   &hufVal, &hufBits);
    huf_l_run  (&b4Y2,   &hufVal, &hufBits);
    huf_cr_run (&b4U,    &hufVal, &hufBits);
    huf_cb_run (&b4V,    &hufVal, &hufBits);

    moretokens = moretokens + hufVal.len();

    fdump_run  (&hufVal, &hufBits);
  }

  fdump_finish();

  if (argc==4 && ~strcmp(argv[3], "-debug")) {
    scanR  .disp_stats();
    scanG  .disp_stats();
    scanB  .disp_stats();
    b1R    .disp_stats();
    b1G    .disp_stats();
    b1B    .disp_stats();
    rgbY   .disp_stats();
    rgbU   .disp_stats();
    rgbV   .disp_stats();
    b2Y1   .disp_stats();
    b2Y2   .disp_stats();
    b2U    .disp_stats();
    b2V    .disp_stats();
    dctY1e .disp_stats();
    dctY1c .disp_stats();
    dctY1r .disp_stats();
    dctY1  .disp_stats();
    dctY2e .disp_stats();
    dctY2c .disp_stats();
    dctY2r .disp_stats();
    dctY2  .disp_stats();
    dctUe  .disp_stats();
    dctUc  .disp_stats();
    dctUr  .disp_stats();
    dctU   .disp_stats();
    dctVe  .disp_stats();
    dctVc  .disp_stats();
    dctVr  .disp_stats();
    dctV   .disp_stats();
    qY1    .disp_stats();
    qY2    .disp_stats();
    qU     .disp_stats();
    qV     .disp_stats();
    b4Y1   .disp_stats();
    b4Y2   .disp_stats();
    b4U    .disp_stats();
    b4V    .disp_stats();
    hufVal .disp_stats();
    hufBits.disp_stats();
  }

  return 0;
}
